#ifndef __CVECTOR_HPP__
#define __CVECTOR_HPP__

#include <iostream>
#include <boost\random\uniform_real.hpp>
#include <boost\random\variate_generator.hpp>
#include <boost\random.hpp>
#include "..\Precision.h"


template <typename T>
class CVector{
public:
	enum VECTOR_TYPE {RANDOM};
	enum Exceptions {SizeMismatch};
	CVector(const CVector & other);
	CVector();
	CVector(unsigned n);
	CVector(unsigned n, VECTOR_TYPE t,T from=-1,T to=1);
	CVector(unsigned n, T val);
	
	CVector operator + (const CVector & other);
	CVector operator - (const CVector & other);
	CVector operator / (const T c);
	CVector operator * (const T c);
	T operator * (const CVector<T> & v);
	
	CVector & operator += (const CVector & other);
	CVector & operator -= (const CVector & other);
	CVector & operator /= (const T c);
	CVector & operator *= (const T c);

	T maxNorm();
	T norm2();
	T differenceMaxNorm(const CVector & other);

	CVector & operator =(const CVector & other); 
	void printVec(unsigned step=1);
	void printVecToFile(const char * fileName,uint size=1);
	

	unsigned const getSize ()const {return _size;}
	
	T & operator [] (unsigned i){return _vec[i];}
	T & operator [] (unsigned i) const {return _vec[i];}
	
	~CVector();
private:
	unsigned _size;
	T * _vec;
};

template<typename T>
CVector<T>::CVector(unsigned n)
{
	_vec=new T[n];
	_size=n;
}

template<typename T>
CVector<T>::CVector()
{
	_vec=0;
	_size=0;
}

template<typename T>
CVector<T>::CVector(const CVector & other)
{
	_vec=new T[other._size];
	_size=other._size;

	for(uint i=0;i<_size;++i)
	{
		_vec[i]=other._vec[i];
	}
}

template<typename T>
CVector<T>::CVector(unsigned n, T val)
{
	_vec=new T[n];
	_size=n;
	for(unsigned i=0;i<_size;++i)
	{
		
		_vec[i]=val;
	}
}

template<typename T>
CVector<T>::CVector(unsigned n, VECTOR_TYPE t,T from,T to)
{
	if(RANDOM==t)
	{
		_vec=new T[n];
		_size=n;
		boost::mt19937 rng;
		boost::uniform_real<T> valFrom(from,to);
		boost::variate_generator<boost::mt19937&, boost::uniform_real<> > ran(rng, valFrom);
	
		for(unsigned i=0;i<_size;++i)
		{
			_vec[i]=ran();
		}
	}
}

template <typename T>
T  CVector<T>::differenceMaxNorm(const CVector<T> & other)
{
	T ret=real_abs((*this)[0]-other[0]);
	for(uint i=1;i<_size;++i)
	{
		T tmp=real_abs((*this)[i]-other[i]);
		if(ret<tmp)
			ret=tmp;
	}
	return ret;
}


template <typename T>
T  CVector<T>::operator*(const CVector<T> & v)
{
	T ret=0;
	for(uint i=0;i<_size;++i)
	{
		ret+=this->_vec[i]*v[i];
	}
	return ret;
}


template <typename T>
CVector<T> & CVector<T>::operator=(const CVector & other)
{
	if(_vec!=other._vec)
	{
		if( _vec!=0 && _size!=other._size)
		{
			delete _vec;
			_vec=new T[other._size];
			_size=other._size;

		}

		if(_vec==0)
		{
			_vec=new T[other._size];
			_size=other._size;
		}

		for(uint i=0;i<_size;++i)
		{
			_vec[i]=other._vec[i];
		}
	}
	return (*this);
}

template<typename T>
void CVector<T>::printVec(unsigned step=1)
{
	unsigned st=_size/step;
	st==0?st=1:st;
	for(unsigned i=0;i<_size;i+=st)
	{
		std::cout<<i<<"  :  "<<_vec[i]<<std::endl;
	}
}


template<typename T>
void CVector<T>::printVecToFile(const char * fileName, uint size)
{
	ofstream out(fileName);
	out<<size<<endl;
	for(unsigned i=0;i<size;++i)
	{
		out<<_vec[i]<<std::endl;
	}
	out.close();
}



template <typename T>
CVector<T> CVector<T>::operator+(const CVector & other)
{
	
	if(this->_size!=other._size)
		throw SizeMismatch;
	CVector<T> ret(_size);

	for(uint i=0;i<_size;++i)
	{
		ret[i]=other._vec[i]+_vec[i];
	}
	return ret;
}

template <typename T>
CVector<T> CVector<T>::operator-(const CVector & other)
{
	
	if(this->_size!=other._size)
		throw SizeMismatch;
	CVector<T> ret(_size);

	for(uint i=0;i<_size;++i)
	{
		ret[i]=_vec[i]-other._vec[i];
	}
	return ret;
}

template <typename T>
CVector<T> CVector<T>::operator*(const T c)
{	
	CVector<T> ret(_size);

	for(uint i=0;i<_size;++i)
	{
		ret[i]=_vec[i]*c;
	}
	return ret;
}

template <typename T>
CVector<T> CVector<T>::operator/(const T c)
{
	
	CVector<T> ret(_size);

	for(uint i=0;i<_size;++i)
	{
		ret[i]=_vec[i]/c;
	}
	return ret;
}

template <typename T>
CVector<T> & CVector<T>::operator+=(const CVector & other)
{
	if(this->_size!=other._size)
		throw SizeMismatch;
	for(uint i=0;i<_size;++i)
	{
		_vec[i]+=other._vec[i];
	}
	return (*this);
}

template <typename T>
CVector<T> & CVector<T>::operator-=(const CVector & other)
{
	if(this->_size!=other._size)
		throw SizeMismatch;

	for(uint i=0;i<_size;++i)
	{
		_vec[i]-=other._vec[i];
	}
	return (*this);
}

template <typename T>
CVector<T> & CVector<T>::operator /=(const T c)
{
	for(uint i=0;i<_size;++i)
	{
		_vec[i]/=c;
	}
	return (*this);
}

template <typename T>
CVector<T> & CVector<T>::operator *=(const T c)
{
	for(uint i=0;i<_size;++i)
	{
		_vec[i]*=c;
	}
	return (*this);
}

template <typename T>
T  CVector<T>::maxNorm()
{
	T ret=real_abs(_vec[0]);
	for(unsigned i=1;i<_size;++i)
	{
		if(real_abs(_vec[i])>ret)
			ret=real_abs(_vec[i]);
	}
	return ret;
}

template <typename T>
T  CVector<T>::norm2()
{
	T ret=0;
	for(unsigned i=0;i<_size;++i)
	{
			ret+=_vec[i]*_vec[i];
	}
	return real_sqrt(ret);
}

template<typename T>
CVector<T>::~CVector()
{
	if(0!=_vec)
		delete [] _vec;
}





#endif